package test.cumulativeProtocol.codec;

import java.nio.charset.Charset;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

import com.mahjong.server.common.Common;
import com.mahjong.server.mina.protocol.DataBuf;
import com.mahjong.server.mina.protocol.MessageProtocol;

/**
 * 解码器(继承CumulativeProtocolDecoder，处理粘包，断包问题)
 * 
 * @author Simple
 * @date 2013-5-21 下午04:07:50
 * @Description TODO
 */
public class TProtocolDecoder extends CumulativeProtocolDecoder {

  private Charset charset;

  private final AttributeKey CONTEXT=new AttributeKey(getClass(), "context");

  public TProtocolDecoder(Charset charset) {
    this.setCharset(charset);
  }

  /**
   * 解码，需要处理粘包，断包问题
   * 
   * 用Context来临时存放不完整数据，待数据达到可解码条件时进行解码
   */
  @Override
  protected boolean doDecode(IoSession session, IoBuffer buf, ProtocolDecoderOutput out) throws Exception {
    // 获取context
    Context context=getContext(session);
    // 第一次读
    if(context.getLength() == 0) {
      byte tag=buf.get();
      int length=buf.getInt();// 获取协议体长度
      context.setTag(tag);
      context.setLength(length);// set协议体总长度
    }
    context.setMatchLength(context.getMatchLength() + buf.remaining());// set可读协议体长度
    // 还有剩余数据
    if(buf.hasRemaining()) {
      IoBuffer tempBuf=context.getBuffer();
      tempBuf.put(buf);// 追加buf
      if(context.getMatchLength() >= context.getLength()) {// 可解码
        // 拿出协议体数据
        byte[] dataBody=new byte[context.getLength()];
        context.getBuffer().flip();// pos置0，为读数据做准备
        context.getBuffer().get(dataBody);
        // 协议号+协议体 BUF
        IoBuffer dataBuf=IoBuffer.allocate(100).setAutoExpand(true);
        dataBuf.put(dataBody);
        dataBuf.flip();
        // 封装MP对象
        MessageProtocol mp=new MessageProtocol();
        mp.setTag(context.getTag());
        if(mp.getTag() == Common.Req) {
          short protocolNum=dataBuf.getShort();// 协议编码
          mp.setProtocolNum(protocolNum);
          mp.setDataBuf(new DataBuf(1024, dataBuf));
        }
        out.write(mp);
        return true;
      } else {// 数据不完整，不能解码
        context.setBuffer(tempBuf);
      }
    }
    return false;
  }

  public void setCharset(Charset charset) {
    this.charset=charset;
  }

  public Charset getCharset() {
    return charset;
  }

  /**
   * 定义一个内部类，用来封转当前解码器中存在的粘包，断包问题造成的一些公共数据/不完整数据，主要是用于大数据解析
   * 
   * @author Simple
   * @date 2013-5-21 下午04:48:08
   * @Description TODO
   */
  private class Context {

    private byte tag;

    private int length=0;// 数据长度

    private IoBuffer buffer;

    private int matchLength=0;// 已接收数据长度

    public Context() {
      this.buffer=IoBuffer.allocate(1024).setAutoExpand(true);
    }

    public void setBuffer(IoBuffer buffer) {
      this.buffer=buffer;
    }

    public IoBuffer getBuffer() {
      return buffer;
    }

    public void setLength(int length) {
      this.length=length;
    }

    public int getLength() {
      return length;
    }

    public void setMatchLength(int matchLength) {
      this.matchLength=matchLength;
    }

    public int getMatchLength() {
      return matchLength;
    }

    public void setTag(byte tag) {
      this.tag=tag;
    }

    public byte getTag() {
      return tag;
    }
  }

  /**
   * 获取context
   * 
   * @param session
   * @return
   */
  public Context getContext(IoSession session) {
    Context context=(Context)session.getAttribute(CONTEXT);
    if(context == null) {
      context=new Context();
      session.setAttribute(CONTEXT, context);
    }
    return context;
  }
}
